package exchange

import (
	"global/libs/structs"
	"fmt"
	"global/constants"
	"time"
	"github.com/gorilla/websocket"
	"github.com/ngaut/log"
	"encoding/json"
	"strings"
	"global/core"
	"github.com/garyburd/redigo/redis"
)

type Binance struct {
	Pool 			*redis.Pool
	Slug 			string
	Symbols 		[]structs.Symbol
}

// 币安获取ticker数据结构
type BinanceTickerData struct {
	EventType 				string		`json:"e"`
	EventTime 				int64 		`json:"E"`
	Symbol 					string 		`json:"s"`
	PriceChange 			string		`json:"p"`
	PricePercent 			string 		`json:"P"`
	WeightedAveragePrice	string		`json:"w"`
	PreviousClosePrice 		string		`json:"x"`
	CurrentClosePrice 		string 		`json:"c"`
	CloseTradeQuantity 		string 		`json:"Q"`
	BestBidPrice 			string 		`json:"b"`
	BestBidQuantity 		string 		`json:"B"`
	BestAskPrice 			string 		`json:"a"`
	BestAskQuantity 		string 		`json:"A"`
	OpenPrice 				string		`json:"o"`
	HighPrice 				string 		`json:"h"`
	LowPrice 				string 		`json:"l"`
	BaseVolume 				string 		`json:"v"`	// 量
	QuoteVolume 			string 		`json:"q"`	// 额
	StaticOpenTime 			int64		`json:"O"`
	StaticCloseTime 		int64		`json:"C"`
	FirstTradeId 			int 		`json:"F"`
	LastTradeId 			int 		`json:"L"`
	Count 					uint64 		`json:"n"`	// 笔数
}

func NewBinanceTicker(pool *redis.Pool, slug string, symbols []structs.Symbol) InterfaceTicker {
	return &Binance{
		Pool: 		pool,
		Slug: 		slug,
		Symbols: 	symbols,
	}
}

func (e *Binance) NeedSymbols() bool {
	return true
}

func (e *Binance) ListenAndAccept() error {
	url := fmt.Sprintf("%s/ws/!ticker@arr", constants.BinanceWebsocketBaseUrl)
	for {
		c, _, err := websocket.DefaultDialer.Dial(url, nil)
		if err != nil {
			log.Errorf("Error: socket dial failed, msg: %s", err.Error())
			time.Sleep(5 * time.Second)
			continue
		}

		for {
			// listen
			_, message, err := c.ReadMessage()
			if err != nil {
				if websocket.IsUnexpectedCloseError(err) {
					break
				}
				continue
			}

			go func(m []byte) {
				if err := e.handlerMessage(m); err != nil {
					log.Errorf("Error, handler message failed, msg: %s", err.Error())
				}
			}(message)
		}
	}
}

func (e *Binance) handlerMessage(message []byte) error {

	var data []BinanceTickerData
	err := json.Unmarshal(message, &data)
	if err != nil {
		return err
	}

	tickers := e.formatTicker(data)

	err = core.SaveTicker(e.Pool, e.Slug, tickers)
	if err != nil {
		return err
	}

	return nil
}

func (e *Binance) formatTicker(data []BinanceTickerData) []structs.Ticker {
	tickers := make([]structs.Ticker, 0)

	var symbols map[string]structs.Symbol
	symbols = make(map[string]structs.Symbol)
	for _, s := range e.Symbols {
		key := strings.ToUpper(s.From + s.To)
		symbols[key] = s
	}

	for _, v := range data {

		var symbol structs.Symbol
		var ok bool
		if symbol, ok = symbols[v.Symbol]; !ok {
			continue
		}

		tickers = append(tickers, structs.Ticker{
			Symbol:		strings.ToLower(symbol.From + "_" + symbol.To),
			From: 		strings.ToLower(symbol.From),
			To: 		strings.ToLower(symbol.To),
			Amount:		v.QuoteVolume,
			Volume: 	v.BaseVolume,
			Percent: 	v.PricePercent,
			Open: 		v.OpenPrice,
			High: 		v.HighPrice,
			Low: 		v.LowPrice,
			Close: 		v.CurrentClosePrice,
			Number: 	v.Count,
			PriceCny: 	"",
			PriceUsd: 	"",
		})
	}
	return tickers
}
